-
Notifications
You must be signed in to change notification settings - Fork 216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Upgrade modules and wallet contracts to solidity 0.6.10 #114
Conversation
d1fed32
to
4bfc474
Compare
4ecbf58
to
5c4cf40
Compare
package.json
Outdated
@@ -57,10 +61,10 @@ | |||
"inquirer": "^7.0.0", | |||
"istanbul": "^0.4.5", | |||
"node-fetch": "^2.3.0", | |||
"openzeppelin-solidity": "2.3.0", | |||
"openzeppelin-solidity": "3.0.1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though both openzeppelin-solidity
and @openzeppelin/contracts
npm repositories are still being updated, @openzeppelin/contracts
seems too be the preferred repo now (it's the one mentioned on their Github page).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had no idea about the new one. Will update.
contracts-test/LegacyBaseWallet.sol
Outdated
@@ -24,7 +24,7 @@ pragma solidity ^0.5.4; | |||
|
|||
/** | |||
* @dev Utility method to recover any ERC20 token that was sent to the | |||
* module by mistake. | |||
* module by mistake. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not related to this PR but this should say "contract" not "module"
contracts-test/TestERC20.sol
Outdated
_mint(_initialAccounts[i], _supply * 10**uint(_decimals)); | ||
super._setupDecimals(_decimals); | ||
for (uint i = 0; i < _initialAccounts.length; i++) { | ||
super._mint(_initialAccounts[i], _supply * 10**uint(_decimals)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is super.
required here? You didn't use it in TestERC721.mint()
Perhaps we should prefix all contracts in |
} | ||
} | ||
|
||
receive() external payable { | ||
emit Received(msg.value, msg.sender, msg.data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably inconsequential but maybe worth notifying the clients that whereas before Received
could not have been emitted with both msg.value == 0
and msg.data == 0
, it is now possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't work out what's right for this event. Here it is emitted for 0 values while in the Proxy it was not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes the previous implementation was confusing. I think it would have made more sense to check if (msg.data.length == 0) { emit ... }
in the Proxy instead of if (msg.data.length == 0 && msg.value > 0) { emit ... }
. The former is equivalent to your new implementation, which I think is good.
contracts/wallet/BaseWallet.sol
Outdated
} | ||
} | ||
} | ||
|
||
receive() external payable { | ||
emit Received(msg.value, msg.sender, msg.data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can remove this event emission and leave receive()
empty. If I'm not mistaken receive()
in BaseWallet
will only ever be called in our tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've made Receive
only emit on non-zero values 12c79ec
Shouldn't we move the |
@olivdb @juniset regarding your comments on the |
9994690
to
1a4273d
Compare
I would remove the |
6a65656
to
4a263b2
Compare
13cb544
to
4eb4581
Compare
@@ -130,7 +130,6 @@ contract TransferManager is OnlyOwnerModule, BaseTransfer, LimitManager { | |||
} | |||
} | |||
|
|||
// TODO: We should inherit the OnlyOwnerModule implementation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably tried the same things but I would have expected to be able to make TransferManager.addModule()
call OnlyOwnerModule.addModule(_wallet, _module);
instead of duplicating OnlyOwnerModule
's code. Or alternatively, make OnlyOwnerModule
be the last contract inherited by TransferManager
(instead of LimitManager
) and use super.addModule(_wallet, _module);
in TransferManager.addModule()
. But none of these solutions seem to work for me. Is that a Solidity bug or am I missing something?
Also, if we're going to duplicate OnlyOwnerModule
's code, let's maybe change "BM: module is not registered" to "TM: module is not registered".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's because addModule
is external
, see Chris's explanation in this discussion ethereum/solidity#3881 (comment)
We have the option of removing addModule
from the IModule
interface, make it public and then use super
. Atm it's not used off that interface so I could go down that route. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally think it is important to keep the addModule
method in the IModule
interface as it will protect us from future mistake.
Guaranteeing that every module can add another module combined with the fact that a wallet needs at least one module ensures the wallet can never be in a blocked state (in the sense of not being upgradable).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yet another way is to switch IModule
from an interface
to abstract contract
which allows public functions with no implementation at the same time as mandating all functions to be implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that works for me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could keep IModule
as-is but make addModule
in BaseModule
and OnlyOwnerModule
be public
instead of external
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's also an option. I don't feel strongly for either of the two. I'll just go with your version as it causes less changes to current state. Let me know if that's okay
} | ||
} | ||
} | ||
|
||
receive() external payable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is not needed and can be removed entirely. It will never be reached from the Proxy (since calls to the Proxy with msg.data == 0
are not forwarded to the BaseWallet).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's actually used in tests as we're not creating wallets that are Proxies but just new-ing up a BaseWallet
instance. It's arguable whether this is correct and we shouldn't be testing with a proper Proxy-based wallet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It helps fix the coverage problem though as I can remove the logic of receive
which allows coverage to pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removing that method won't break the tests as they currently stand. The fallback()
of BaseWallet will be executed instead, so I believe we can remove receive()
regardless of whether we decide to rewrite the tests to use Proxy
or not
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I does break the tests as fallback won't execute when there's no calldata received. That was the whole point of splitting the fallback function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per the doc:
A payable fallback function is also executed for plain Ether transfers, if no receive Ether function is present. [...]
https://solidity.readthedocs.io/en/v0.6.10/contracts.html#fallback-function
I also verified in Remix that the following contract is able to receive ether via empty calldata:
pragma solidity ^0.6.10;
contract FallbackTest {
fallback() external payable {}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay it's not failing because plain ether is not accepted but because the call to fallback
costs more than 21000 gas. https://app.circleci.com/pipelines/github/argentlabs/argent-contracts/707/workflows/26010b94-04c4-4a0c-ad62-c9cbe0e39afe/jobs/897
I can't think of any way to completely remove the receive
function off BaseWallet
really but feel free to push any changes you think are possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. I don't love the idea of having a receive()
function in BaseWallet
that is only there for tests. On the other hand having to use a Proxy + BaseWallet in all our tests feels a bit heavy. Maybe using a BaseWalletTest
in our tests, that inherits from BaseWallet
and only adds an empty receive()
to BaseWallet
could be a middle ground? If not, let's just keep the empty receive()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about this more, testing with a Proxy + BaseWallet is probably the right move. We could maybe add a createWallet()
method to test-manager.js
that deploys both contracts + wraps the proxy with the BaseWallet abi.
The deployment script |
c3cd35f
to
c03e3cc
Compare
and instead move all logic to standalone FakeWallet contract
3e9b301
to
65e4074
Compare
Why doesn't |
I think |
Because otherwise we'll have cross references between legacy folders, in this case 1.3.0 importing contracts from 1.5.0. I think we should keep legacy copies atomic to a given release, otherwise it will likely get very tangled up. |
|
Upgrades contracts from
/wallet
and/modules
to Solidity 0.6.10 while/infrastructure
contracts remain on 0.5.4 except for theWalletFactory
andTokenPriceProvider
which are also upgraded to 0.6.10 (for that purpose I've had to move them out of /infrastructure to be compiled with the rest of the upgraded contracts until LimeChain/etherlime#325 is fixed.Overlapping areas of logic were abstracted into interfaces with wider range compilation pragma i.e.
pragma solidity >=0.5.4 <0.7.0;
due to the fact there are no breaking changes to interfaces between the two major releases.Includes refactoring that pulls apart the dependencies between storage contracts,
BaseWallet
andModule
in order to allow storage contracts to remain on solc 0.5.4. Interactions between these layers is done via theIModule
,IWallet
,IGuardianStorage
andITransferStorage
interfaces.Also upgraded the openzeppelin-contracts library to 3.0.1
The following changes in gas consumption for benchmark tests is observed between releases:
Additionally we've removed the multiple inheritance in modules, e.g.
contract NftTransfer is BaseModule, RelayerModule, OnlyOwnerModule
->contract NftTransfer is OnlyOwnerModule
as the former causes the following compile errors caused byBaseModule
,RelayerModule
,OnlyOwnerModule
all implementing versions ofaddModule
andcheckAndUpdateUniqueness
methods.Note that due to crytic/slither#405 we've had to disable
slither
analysis on the wallet contracts. Logged this in trello to remind us to reenable it onceslither
supports analysis of the new 0.6. syntax.